/*
 * Copyright (c) 2009, David A. Freels Sr.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package net.whippetcode.plugins.intellij.hudson;

import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.peer.PeerFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.intellij.util.xmlb.XmlSerializerUtil;
import net.whippetcode.plugins.intellij.hudson.ui.ConsoleViewAdapter;
import net.whippetcode.plugins.intellij.hudson.ui.StatusIndicator;
import net.whippetcode.plugins.intellij.hudson.ui.changeset.ChangeSetController;
import net.whippetcode.plugins.intellij.hudson.ui.job.HudsonJobController;
import net.whippetcode.plugins.intellij.hudson.ui.test.HudsonTestController;
import net.whippetcode.plugins.intellij.hudson.util.IOUtil;
import net.whippetcode.plugins.intellij.hudson.util.LogUtil;
import net.whippetcode.util.task.ITask;
import net.whippetcode.util.task.TaskManager;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;

import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import java.awt.BorderLayout;
import java.awt.Image;
import java.util.Timer;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

/**
 * This is the main entry point.
 *
 * @author David A. Freels Sr.
 */
@State(name = "HudsonMonitorComponent",
	 storages = @Storage(id = "HudsonMonitorSettings", file = "$PROJECT_FILE$"))
public class HudsonMonitorComponent implements ProjectComponent, Configurable, PersistentStateComponent<HudsonMonitorSettings>
{
	public static final String TOOL_WINDOW_ID = "Hudson";
	public static final String CONSOLE_WINDOW_ID = "Hudson Messages";

	private HudsonMonitorConfiguration form;
	private Project project;
	private StatusIndicator statusIndicator;
	private Timer timer;
	private HudsonMonitorSettings settings;
	private ImageIcon icon;
	private ImageIcon toolBarIcon;
	private HudsonJobController jobController;

	public HudsonMonitorComponent(Project project) throws Exception
	{
		this.project = project;
		statusIndicator = new StatusIndicator();
		settings = new HudsonMonitorSettings();

		Image logo = ImageIO.read(this.getClass().getResourceAsStream("/net/whippetcode/plugins/intellij/hudson/images/hudson_logo.gif"));
		icon = new ImageIcon(logo);
		logo = ImageIO.read(this.getClass().getResourceAsStream("/net/whippetcode/plugins/intellij/hudson/images/hudson_logo_small.png"));
		toolBarIcon = new ImageIcon(logo);

		statusIndicator.updateTooltip("Not Initialized");
	}

	public void initComponent()
	{


	}

	public void disposeComponent()
	{
	}

	@NotNull
	public String getComponentName()
	{
		return "HudsonMonitorComponent";
	}

	@Nls
	public String getDisplayName()
	{
		return "Hudson Build Monitor";
	}

	public Icon getIcon()
	{
		return icon;
	}

	public String getHelpTopic()
	{
		return null;
	}

	public JComponent createComponent()
	{
		if (form == null)
		{
			form = new HudsonMonitorConfiguration();
		}
		return form.getRootPanel();
	}

	public boolean isModified()
	{
		return form != null && form.isModified(settings);
	}

	public void apply() throws ConfigurationException
	{
		if (form != null)
		{
			// Get data from form to component
			form.getData(settings);
			applyGlobalSettings();
			jobController.update(settings);
			restartTimer();
		}
	}

	/**
	 * Cancels the current polling timer and starts a new timer.
	 */
	private void restartTimer()
	{
		if (settings.getServerAddress() == null)
		{
			return;
		}

		if (timer != null)
		{
			timer.cancel();
		}

		timer = new Timer();
		timer.schedule(new ServerPollingTask(jobController), 0, Integer.parseInt(settings.getRefreshInterval()) * 60000);
	}

	public void reset()
	{
		if (form != null)
		{
			// Reset form data from component
			form.setData(settings);
		}
	}

	public void disposeUIResources()
	{
		form = null;
	}

	public void projectOpened()
	{
		applyGlobalSettings();
		ContentFactory contentFactory = PeerFactory.getInstance().getContentFactory();
		ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(this.project);

		ToolWindow toolWindow = toolWindowManager.registerToolWindow(TOOL_WINDOW_ID, false, ToolWindowAnchor.LEFT);
		ToolWindow consoleWindow = toolWindowManager.registerToolWindow(CONSOLE_WINDOW_ID, false, ToolWindowAnchor.BOTTOM);

		ChangeSetController changeSetController = new ChangeSetController();
		ConsoleView view = TextConsoleBuilderFactory.getInstance().createBuilder(project).getConsole();
		Content content = contentFactory.createContent(view.getComponent(), "Messages", false);
		consoleWindow.getContentManager().addContent(content);
		consoleWindow.setIcon(toolBarIcon);

		content = contentFactory.createContent(changeSetController.getView(), "Changes", false);
		consoleWindow.getContentManager().addContent(content);

		HudsonTestController testController = new HudsonTestController(new ConsoleViewAdapter(view, consoleWindow));
		TaskManager taskManager = new TaskManager();
		taskManager.setCompletionService(new ExecutorCompletionService<ITask>(new ThreadPoolExecutor(10, 10, 300, TimeUnit.SECONDS,
			 new LinkedBlockingQueue<Runnable>())));
		jobController = new HudsonJobController(this.settings, this.statusIndicator, testController, changeSetController, taskManager);

		JPanel panel = new JPanel(new BorderLayout());

		JSplitPane splitPane = new JSplitPane();
		panel.add(splitPane, BorderLayout.CENTER);

		splitPane.setDividerLocation(300);
		splitPane.setResizeWeight(0.5);
		splitPane.setBottomComponent(testController.getView());
		splitPane.setTopComponent(jobController.getView());
		splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);

		content = contentFactory.createContent(panel, "Hudson", false);
		toolWindow.getContentManager().addContent(content);
		toolWindow.setIcon(toolBarIcon);

		StatusBar sb = WindowManager.getInstance().getStatusBar(this.project);
		sb.addCustomIndicationComponent(statusIndicator);
		restartTimer();
	}

	private void applyGlobalSettings()
	{
		IOUtil.setHudsonSettings(settings);
		if (settings.isDebugCheckBox())
		{
			LogUtil.setLoglevel(Level.FINEST);
		}
	}

	public void projectClosed()
	{
		StatusBar sb = WindowManager.getInstance().getStatusBar(this.project);
		sb.removeCustomIndicationComponent(statusIndicator);
		if (timer != null)
		{
			timer.cancel();
			timer = null;
		}

		ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(this.project);
		toolWindowManager.unregisterToolWindow(TOOL_WINDOW_ID);
		toolWindowManager.unregisterToolWindow(CONSOLE_WINDOW_ID);
	}

	public HudsonMonitorSettings getState()
	{
		return settings;
	}

	public void loadState(HudsonMonitorSettings o)
	{
		XmlSerializerUtil.copyBean(o, settings);
	}
}